package com.nebula.salary.portal.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nebula.salary.common.constants.Const;
import com.nebula.salary.common.constants.ReturnMessage;
import com.nebula.salary.common.exception.CustomException;
import com.nebula.salary.common.result.ServerResponse;
import com.nebula.salary.common.utils.RedisUtil;
import com.nebula.salary.common.utils.SecurityUtil;
import com.nebula.salary.model.dto.RoleDto;
import com.nebula.salary.model.dto.RoleQueryDto;
import com.nebula.salary.model.pojo.Role;
import com.nebula.salary.model.pojo.RoleNavigation;
import com.nebula.salary.model.pojo.Salary;
import com.nebula.salary.model.pojo.User;
import com.nebula.salary.model.vo.PageResult;
import com.nebula.salary.model.vo.RoleVo;
import com.nebula.salary.model.vo.SalaryVo;
import com.nebula.salary.portal.mapper.RoleMapper;
import com.nebula.salary.portal.mapper.RoleNavigationMapper;
import com.nebula.salary.portal.mapper.UserMapper;
import com.nebula.salary.portal.service.IRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author codex
 * @since 2022-01-24
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {
    
    @Resource
    private RoleNavigationMapper roleNavigationMapper;
    
    @Resource
    private UserMapper userMapper;
    
    @Resource
    private RedisUtil redisUtil;
    
    /**
     * 新增角色
     *
     * @param roleDto 角色数据传输类
     * @return 新增结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServerResponse<String> add(RoleDto roleDto) {
        Role role = new Role();
        BeanUtils.copyProperties(roleDto, role);
        role.setUser(SecurityUtil.getLoginUser().getUser().getRealName());
        
        if (!role.getRoleSecurity().startsWith(Const.ROLE_SECURITY_PREFIX)) {
            role.setRoleSecurity(Const.ROLE_SECURITY_PREFIX + role.getRoleSecurity());
        }
        
        if (baseMapper.insert(role) >= 1) {
            roleDto.setRoleId(role.getRoleId());
            insertRoleMenu(roleDto);
            return ServerResponse.createBySuccessMessage(ReturnMessage.INSERT_SUCCESS);
        }
        
        return ServerResponse.createByErrorMessage(ReturnMessage.INSERT_ERROR);
    }
    
    /**
     * 获取有权管理的角色列表
     *
     * @param page     当前页码
     * @param pageSize 每页条数
     * @param role     查询条件
     * @return 角色列表
     */
    @Override
    public ServerResponse<PageResult> getRolesByPage(Long page, Long pageSize, RoleQueryDto role) {
        OptionalInt roleMaxLevel = SecurityUtil.getLoginUser().getUser().getRoles().stream()
                .mapToInt(Role::getRoleLevel).max();
        Page<RoleVo> roleVoPage = new Page<>(page, pageSize);
        IPage<RoleVo> pages = baseMapper.getRolesByPage(roleVoPage, roleMaxLevel.getAsInt(), role);
        
        PageResult pageResult = new PageResult(pages.getTotal(), pages.getRecords().size(), pages.getRecords());
        
        return ServerResponse.createBySuccess(pageResult);
    }
    
    /**
     * 验证数据输入的正确性
     */
    @Override
    public void checkData(RoleDto roleDto) {
        if (roleDto == null) {
            throw new CustomException(ReturnMessage.ILLEGAL_REQUEST);
        }
        
        if (Strings.isNullOrEmpty(roleDto.getRoleName())) {
            throw new CustomException(ReturnMessage.ROLE_NAME_CAN_NOT_BE_EMPTY);
        }
        
        if (Strings.isNullOrEmpty(roleDto.getRoleSecurity())) {
            throw new CustomException(ReturnMessage.ROLE_SECURITY_CAN_NOT_BE_EMPTY);
        }
        
        if (roleDto.getRoleLevel() == null) {
            throw new CustomException(ReturnMessage.ROLE_LEVEL_CAN_NOT_BE_EMPTY);
        }
        
        if (roleDto.getRoleLevel() < 1 || roleDto.getRoleLevel() > 5) {
            throw new CustomException(ReturnMessage.ROLE_LEVEL_ERROR);
        }
    }
    
    /**
     * 校验角色名称是否唯一
     *
     * @param roleDto 角色信息
     * @return 结果
     */
    @Override
    public String checkRoleNameUnique(RoleDto roleDto) {
        Integer roleId = roleDto.getRoleId() == null ? -1 : roleDto.getRoleId();
        Role role = baseMapper.selectOne(new QueryWrapper<Role>().eq(Role.ROLE_NAME, roleDto.getRoleName()));
        if (role != null && role.getRoleId().intValue() != roleId.intValue()) {
            return Const.NOT_UNIQUE;
        }
        
        return Const.UNIQUE;
    }
    
    /**
     * 校验角色权限是否唯一
     *
     * @param roleDto 角色信息
     * @return 结果
     */
    @Override
    public String checkRoleSecurityUnique(RoleDto roleDto) {
        Integer roleId = roleDto.getRoleId() == null ? -1 : roleDto.getRoleId();
        Role role = baseMapper.selectOne(new QueryWrapper<Role>().eq(Role.ROLE_SECURITY, roleDto.getRoleSecurity()));
        if (role != null && role.getRoleId().intValue() != roleId.intValue()) {
            return Const.NOT_UNIQUE;
        }
        
        return Const.UNIQUE;
    }
    
    /**
     * 校验当前用户角色是否有数据权限
     *
     * @param roleId 角色ID
     */
    @Override
    public void checkRoleDataScope(Integer roleId) {
        if (!User.isAdmin(SecurityUtil.getUserId())) {
            // 非超级管理员，对权限进行认证
            Role role = baseMapper.selectRoleByUserId(SecurityUtil.getUserId());
            List<Role> roles = baseMapper.selectRoleListByRoleLevel(role.getRoleLevel());
            if (roles == null || roles.isEmpty()) {
                throw new CustomException("没有权限访问角色数据！");
            }
            
            if (roles.stream().noneMatch(m -> m.getRoleId().equals(roleId))) {
                throw new CustomException("没有权限访问角色数据！");
            }
        }
    }
    
    /**
     * 根据用户ID获取角色信息
     *
     * @param userId 用户ID
     * @return 角色信息
     */
    @Override
    public Role getRolesByUserId(Long userId) {
        return baseMapper.selectRoleByUserId(userId);
    }
    
    /**
     * 修改角色
     *
     * @param roleDto 角色信息
     * @return 修改结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateRole(RoleDto roleDto) {
        // 修改角色
        Role role = new Role();
        BeanUtils.copyProperties(roleDto, role);
        role.setUser(SecurityUtil.getLoginUser().getUser().getRealName());
        
        if (!role.getRoleSecurity().startsWith(Const.ROLE_SECURITY_PREFIX)) {
            role.setRoleSecurity(Const.ROLE_SECURITY_PREFIX + role.getRoleSecurity());
        }
        
        baseMapper.updateById(role);
        
        // 删除角色与菜单的关联
        roleNavigationMapper.deleteRoleNavigationByRoleId(roleDto.getRoleId());
        
        return insertRoleMenu(roleDto);
    }
    
    /**
     * 修改角色菜单权限
     *
     * @param roleDto 角色和菜单信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateRoleNavigation(RoleDto roleDto) {
    
        // 删除缓存中的菜单权限
        redisUtil.deleteObject(Const.MENU_ROLE_KEY + Const.ROLE_SECURITY_PREFIX);
        
        // 删除角色与菜单的关联
        roleNavigationMapper.deleteRoleNavigationByRoleId(roleDto.getRoleId());
        
        return insertRoleMenu(roleDto);
    }
    
    /**
     * 批量删除
     *
     * @param roleIds 角色ID数组
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServerResponse<String> deleteRoles(Integer[] roleIds) {
        for (Integer roleId : roleIds) {
            if (roleId == 1001) {
                throw new CustomException("不允许操作超级管理员角色");
            }
            checkRoleDataScope(roleId);
            Role role = baseMapper.selectById(roleId);
            Integer count = userMapper.selectCount(new QueryWrapper<User>().eq(User.ROLE_ID, roleId));
            if (count > 0) {
                throw new CustomException(String.format("%1$s已分配,不能删除", role.getRoleName()));
            }
        }
        
        // 删除角色与菜单的关联关系
        roleNavigationMapper.deleteBatchRoleIds(roleIds);
        
        return baseMapper.deleteBatchIds(Arrays.asList(roleIds)) > 0
                ? ServerResponse.createBySuccessMessage(ReturnMessage.DELETE_SUCCESS)
                : ServerResponse.createByErrorMessage(ReturnMessage.DELETE_ERROR);
    }
    
    /**
     * 校验角色等级是否超过当前用户所拥有的的等级
     *
     * @param roleDto 角色信息
     * @return 结果
     */
    @Override
    public String checkRoleLevelAllowed(RoleDto roleDto) {
        Integer roleId = roleDto.getRoleId() == null ? -1 : roleDto.getRoleId();
        
        String key = Const.LOGIN_USER_KEY + SecurityUtil.getUsername() + "_" + SecurityUtil.getUserId();
        Role role = redisUtil.getObject(key);
        if (role == null) {
            role = baseMapper.selectRoleByUserId(SecurityUtil.getUserId());
        }
        
        if (role != null && roleId.intValue() != role.getRoleId().intValue() && role.getRoleLevel() >= roleDto.getRoleLevel()) {
            return Const.UNAUTHORIZED;
        }
        
        return Const.NOT_UNAUTHORIZED;
    }
    
    /**
     * 获取有权的所有角色列表
     * 
     * @param role 角色信息
     * @return 角色列表
     */
	@Override
	public List<RoleVo> getPerAllList(RoleQueryDto role) {
		User user = userMapper.selectUserByUsername(SecurityUtil.getUsername());
		Page<RoleVo> page = new Page<>(1, Integer.MAX_VALUE - 1);
		IPage<RoleVo> pages = baseMapper.getRolesByPage(page, user.getRoles().get(0).getRoleLevel(), role);
		return pages.getRecords();
	}
    
    /**
     * 新增角色菜单信息
     *
     * @param roleDto 角色信息
     * @return 新增结果
     */
    private int insertRoleMenu(RoleDto roleDto) {
        int rows = 1;
        
        // 新增角色与菜单关联
        List<RoleNavigation> list = new ArrayList<>();
        for (Integer navId : roleDto.getNavIds()) {
            RoleNavigation roleNavigation = new RoleNavigation();
            roleNavigation.setNavId(navId);
            roleNavigation.setRoleId(roleDto.getRoleId());
            list.add(roleNavigation);
        }
        
        if (list.size() > 0) {
            rows = roleNavigationMapper.batchInsertRoleNavigation(list);
        }
        
        return rows;
    }
}
